from abc import ABC, abstractmethod
from typing import List, Dict, Any, Tuple, Optional
from datetime import datetime, time, date, timedelta
from enum import Enum
import re
import logging
from dataclasses import dataclass

from ..models.audit_models import AuditRequest, RuleResult, RulePriority
from ..utils.logger import get_logger

logger = get_logger(__name__)


class RuleStatus(str, Enum):
    """规则执行状态."""
    PASS = "pass"
    FAIL = "fail"
    WARNING = "warning"
    SKIP = "skip"


@dataclass
class RuleCheckResult:
    """规则检查结果."""
    status: RuleStatus
    score_impact: float
    message: str
    details: Optional[Dict[str, Any]] = None
    execution_time_ms: float = 0.0


class BaseRule(ABC):
    """审核规则基类."""
    
    def __init__(self, rule_id: str, name: str, priority: RulePriority = RulePriority.MEDIUM,
                 weight: float = 1.0, is_blocking: bool = False):
        self.rule_id = rule_id
        self.name = name
        self.priority = priority
        self.weight = weight
        self.is_blocking = is_blocking
        self.is_active = True
    
    @abstractmethod
    def check(self, request: AuditRequest) -> RuleCheckResult:
        """执行规则检查."""
        pass
    
    def _create_result(self, status: RuleStatus, score: float, message: str, 
                      details: Optional[Dict] = None) -> RuleCheckResult:
        """创建规则检查结果."""
        return RuleCheckResult(
            status=status,
            score_impact=score * self.weight,
            message=message,
            details=details or {}
        )


class TimeConflictChecker(BaseRule):
    """时间冲突检查规则."""
    
    def __init__(self):
        super().__init__(
            rule_id="time_conflict",
            name="教室时间冲突检查",
            priority=RulePriority.CRITICAL,
            weight=1.0,
            is_blocking=True
        )
        # 模拟已存在的预订数据库
        self.existing_bookings = self._load_existing_bookings()
    
    def _load_existing_bookings(self) -> List[Dict[str, Any]]:
        """加载现有预订数据（实际应从数据库查询）."""
        return [
            {
                "classroom_id": "A101",
                "start_time": datetime(2024, 12, 15, 9, 0),
                "end_time": datetime(2024, 12, 15, 11, 0),
                "status": "approved"
            },
            {
                "classroom_id": "A102", 
                "start_time": datetime(2024, 12, 15, 14, 0),
                "end_time": datetime(2024, 12, 15, 16, 0),
                "status": "approved"
            }
        ]
    
    def check(self, request: AuditRequest) -> RuleCheckResult:
        """检查时间冲突."""
        start_time = datetime.now()
        
        try:
            conflicts = []
            for booking in self.existing_bookings:
                if (booking["classroom_id"] == request.classroom_id and 
                    booking["status"] == "approved"):
                    
                    # 检查时间重叠
                    if self._times_overlap(
                        request.start_time, request.end_time,
                        booking["start_time"], booking["end_time"]
                    ):
                        conflicts.append(booking)
            
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            
            if conflicts:
                return RuleCheckResult(
                    status=RuleStatus.FAIL,
                    score_impact=-100,  # 完全失败
                    message=f"教室 {request.classroom_id} 在请求时间段内已被预订",
                    details={"conflicts": conflicts},
                    execution_time_ms=execution_time
                )
            else:
                return RuleCheckResult(
                    status=RuleStatus.PASS,
                    score_impact=20,  # 加分
                    message="时间段可用，无冲突",
                    execution_time_ms=execution_time
                )
                
        except Exception as e:
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            logger.error(f"时间冲突检查失败: {str(e)}")
            return RuleCheckResult(
                status=RuleStatus.WARNING,
                score_impact=-10,
                message=f"时间冲突检查异常: {str(e)}",
                execution_time_ms=execution_time
            )
    
    def _times_overlap(self, start1: datetime, end1: datetime, 
                      start2: datetime, end2: datetime) -> bool:
        """检查两个时间段是否重叠."""
        return start1 < end2 and start2 < end1


class ApplicantQualificationChecker(BaseRule):
    """申请人资格验证规则."""
    
    def __init__(self):
        super().__init__(
            rule_id="applicant_qualification",
            name="申请人资格验证",
            priority=RulePriority.HIGH,
            weight=0.8
        )
        # 模拟员工/学生数据库
        self.authorized_users = self._load_authorized_users()
    
    def _load_authorized_users(self) -> Dict[str, Dict[str, Any]]:
        """加载授权用户数据（实际应从数据库/LDAP查询）."""
        return {
            "T001": {"type": "teacher", "department": "计算机学院", "level": "professor"},
            "T002": {"type": "teacher", "department": "数学学院", "level": "associate"},
            "S001": {"type": "student", "department": "计算机学院", "level": "graduate"},
            "S002": {"type": "student", "department": "物理学院", "level": "undergraduate"},
            "A001": {"type": "admin", "department": "教务处", "level": "manager"}
        }
    
    def check(self, request: AuditRequest) -> RuleCheckResult:
        """验证申请人资格."""
        start_time = datetime.now()
        
        try:
            applicant_id = request.applicant_info.get("applicant_id", "")
            user_info = self.authorized_users.get(applicant_id)
            
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            
            if not user_info:
                return RuleCheckResult(
                    status=RuleStatus.FAIL,
                    score_impact=-50,
                    message=f"申请人 {applicant_id} 不在授权用户列表中",
                    execution_time_ms=execution_time
                )
            
            # 根据用户类型给分
            score_map = {
                "admin": 25,
                "teacher": 20, 
                "graduate": 15,
                "undergraduate": 10
            }
            
            user_type = user_info.get("type", "")
            level = user_info.get("level", "")
            
            base_score = score_map.get(user_type, 5)
            
            # 教授和管理员额外加分
            if level in ["professor", "manager"]:
                base_score += 5
            
            return RuleCheckResult(
                status=RuleStatus.PASS,
                score_impact=base_score,
                message=f"申请人资格验证通过 ({user_type}: {level})",
                details={"user_info": user_info},
                execution_time_ms=execution_time
            )
            
        except Exception as e:
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            logger.error(f"申请人资格验证失败: {str(e)}")
            return RuleCheckResult(
                status=RuleStatus.WARNING,
                score_impact=-5,
                message=f"资格验证异常: {str(e)}",
                execution_time_ms=execution_time
            )


class PurposeValidator(BaseRule):
    """使用目的合理性验证规则."""
    
    def __init__(self):
        super().__init__(
            rule_id="purpose_validator",
            name="使用目的合理性检查",
            priority=RulePriority.MEDIUM,
            weight=0.6
        )
        
        # 定义目的分类和关键词
        self.purpose_categories = {
            "academic": {
                "keywords": ["教学", "课程", "讲座", "学术", "研讨", "论文", "答辩", "会议"],
                "score": 15,
                "description": "学术教学活动"
            },
            "research": {
                "keywords": ["研究", "实验", "项目", "讨论", "开发", "创新"],
                "score": 12,
                "description": "科研活动"
            },
            "administrative": {
                "keywords": ["培训", "会议", "总结", "规划", "管理", "行政"],
                "score": 10,
                "description": "行政管理活动"
            },
            "student_activity": {
                "keywords": ["社团", "活动", "比赛", "竞赛", "展示", "表演", "晚会"],
                "score": 8,
                "description": "学生活动"
            },
            "personal": {
                "keywords": ["私人", "个人", "聚会", "娱乐", "游戏"],
                "score": -20,
                "description": "个人私用"
            }
        }
        
        # 可疑关键词
        self.suspicious_keywords = ["商业", "营销", "销售", "盈利", "收费", "私教"]
    
    def check(self, request: AuditRequest) -> RuleCheckResult:
        """验证使用目的合理性."""
        start_time = datetime.now()
        
        try:
            purpose = request.usage_purpose.lower()
            
            # 检查可疑关键词
            for keyword in self.suspicious_keywords:
                if keyword in purpose:
                    execution_time = (datetime.now() - start_time).total_seconds() * 1000
                    return RuleCheckResult(
                        status=RuleStatus.FAIL,
                        score_impact=-30,
                        message=f"使用目的包含不当关键词: {keyword}",
                        details={"suspicious_keyword": keyword},
                        execution_time_ms=execution_time
                    )
            
            # 分类匹配
            matched_categories = []
            total_score = 0
            
            for category, config in self.purpose_categories.items():
                matched_keywords = [kw for kw in config["keywords"] if kw in purpose]
                if matched_keywords:
                    matched_categories.append({
                        "category": category,
                        "matched_keywords": matched_keywords,
                        "score": config["score"],
                        "description": config["description"]
                    })
                    total_score += config["score"]
            
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            
            if not matched_categories:
                return RuleCheckResult(
                    status=RuleStatus.WARNING,
                    score_impact=0,
                    message="使用目的未能明确分类，需要人工审核",
                    details={"purpose": purpose},
                    execution_time_ms=execution_time
                )
            
            # 取最高分的分类作为主要得分
            best_category = max(matched_categories, key=lambda x: x["score"])
            
            status = RuleStatus.PASS if best_category["score"] > 0 else RuleStatus.FAIL
            
            return RuleCheckResult(
                status=status,
                score_impact=best_category["score"],
                message=f"使用目的分类: {best_category['description']}",
                details={"matched_categories": matched_categories},
                execution_time_ms=execution_time
            )
            
        except Exception as e:
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            logger.error(f"使用目的验证失败: {str(e)}")
            return RuleCheckResult(
                status=RuleStatus.WARNING,
                score_impact=-5,
                message=f"目的验证异常: {str(e)}",
                execution_time_ms=execution_time
            )


class EquipmentMatcher(BaseRule):
    """设备需求匹配验证规则."""
    
    def __init__(self):
        super().__init__(
            rule_id="equipment_matcher",
            name="设备需求匹配检查",
            priority=RulePriority.MEDIUM,
            weight=0.5
        )
        
        # 模拟教室设备数据
        self.classroom_equipment = self._load_classroom_equipment()
    
    def _load_classroom_equipment(self) -> Dict[str, List[str]]:
        """加载教室设备信息（实际应从数据库查询）."""
        return {
            "A101": ["投影仪", "音响", "白板", "空调", "网络"],
            "A102": ["投影仪", "白板", "空调", "网络"],
            "B201": ["投影仪", "音响", "白板", "空调", "网络", "话筒", "摄像设备"],
            "C301": ["电脑", "投影仪", "音响", "白板", "空调", "网络"]
        }
    
    def check(self, request: AuditRequest) -> RuleCheckResult:
        """检查设备需求匹配."""
        start_time = datetime.now()
        
        try:
            classroom_id = request.classroom_id
            equipment_needs = request.equipment_needs or []
            
            if not equipment_needs:
                execution_time = (datetime.now() - start_time).total_seconds() * 1000
                return RuleCheckResult(
                    status=RuleStatus.PASS,
                    score_impact=5,
                    message="无特殊设备需求",
                    execution_time_ms=execution_time
                )
            
            available_equipment = self.classroom_equipment.get(classroom_id, [])
            
            matched_equipment = []
            missing_equipment = []
            
            for equipment in equipment_needs:
                if equipment in available_equipment:
                    matched_equipment.append(equipment)
                else:
                    missing_equipment.append(equipment)
            
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            
            if missing_equipment:
                return RuleCheckResult(
                    status=RuleStatus.FAIL,
                    score_impact=-15,
                    message=f"教室缺少必需设备: {', '.join(missing_equipment)}",
                    details={
                        "matched": matched_equipment,
                        "missing": missing_equipment,
                        "available": available_equipment
                    },
                    execution_time_ms=execution_time
                )
            else:
                return RuleCheckResult(
                    status=RuleStatus.PASS,
                    score_impact=10,
                    message="所有设备需求都能满足",
                    details={
                        "matched": matched_equipment,
                        "available": available_equipment
                    },
                    execution_time_ms=execution_time
                )
                
        except Exception as e:
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            logger.error(f"设备匹配检查失败: {str(e)}")
            return RuleCheckResult(
                status=RuleStatus.WARNING,
                score_impact=-3,
                message=f"设备匹配检查异常: {str(e)}",
                execution_time_ms=execution_time
            )


class TimeRangeValidator(BaseRule):
    """时间范围合理性验证规则."""
    
    def __init__(self):
        super().__init__(
            rule_id="time_range_validator",
            name="使用时间合理性检查",
            priority=RulePriority.MEDIUM,
            weight=0.4
        )
        
        # 定义合理时间范围
        self.business_hours = {
            "start": time(8, 0),   # 08:00
            "end": time(22, 0)     # 22:00
        }
        
        # 最大使用时长（小时）
        self.max_duration_hours = 8
        self.min_duration_minutes = 30
    
    def check(self, request: AuditRequest) -> RuleCheckResult:
        """验证时间范围合理性."""
        start_time = datetime.now()
        
        try:
            start_dt = request.start_time
            end_dt = request.end_time
            
            duration = end_dt - start_dt
            duration_hours = duration.total_seconds() / 3600
            duration_minutes = duration.total_seconds() / 60
            
            issues = []
            score = 10  # 基础分
            
            # 检查时间是否在营业时间内
            start_time_only = start_dt.time()
            end_time_only = end_dt.time()
            
            if start_time_only < self.business_hours["start"]:
                issues.append("开始时间早于营业时间")
                score -= 15
            
            if end_time_only > self.business_hours["end"]:
                issues.append("结束时间晚于营业时间")
                score -= 15
            
            # 检查使用时长
            if duration_minutes < self.min_duration_minutes:
                issues.append("使用时长过短")
                score -= 10
            
            if duration_hours > self.max_duration_hours:
                issues.append("使用时长过长")
                score -= 20
            
            # 检查是否跨天
            if start_dt.date() != end_dt.date():
                issues.append("申请跨天使用")
                score -= 25
            
            # 检查是否提前申请
            advance_days = (start_dt.date() - date.today()).days
            if advance_days < 1:
                issues.append("申请时间过于仓促")
                score -= 10
            elif advance_days > 30:
                issues.append("申请时间过于提前")
                score -= 5
            
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            
            if issues:
                status = RuleStatus.WARNING if score > 0 else RuleStatus.FAIL
                return RuleCheckResult(
                    status=status,
                    score_impact=score,
                    message=f"时间安排存在问题: {'; '.join(issues)}",
                    details={
                        "issues": issues,
                        "duration_hours": duration_hours,
                        "advance_days": advance_days
                    },
                    execution_time_ms=execution_time
                )
            else:
                return RuleCheckResult(
                    status=RuleStatus.PASS,
                    score_impact=score,
                    message="时间安排合理",
                    details={
                        "duration_hours": duration_hours,
                        "advance_days": advance_days
                    },
                    execution_time_ms=execution_time
                )
                
        except Exception as e:
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            logger.error(f"时间范围验证失败: {str(e)}")
            return RuleCheckResult(
                status=RuleStatus.WARNING,
                score_impact=-5,
                message=f"时间验证异常: {str(e)}",
                execution_time_ms=execution_time
            )


class ParticipantCountValidator(BaseRule):
    """参与人数合理性验证规则."""
    
    def __init__(self):
        super().__init__(
            rule_id="participant_count_validator",
            name="参与人数合理性检查", 
            priority=RulePriority.MEDIUM,
            weight=0.3
        )
        
        # 模拟教室容量数据
        self.classroom_capacity = {
            "A101": 50,
            "A102": 30,
            "B201": 100,
            "C301": 25
        }
    
    def check(self, request: AuditRequest) -> RuleCheckResult:
        """验证参与人数合理性."""
        start_time = datetime.now()
        
        try:
            classroom_id = request.classroom_id
            participants = request.participants_count
            capacity = self.classroom_capacity.get(classroom_id, 50)  # 默认50人
            
            utilization_rate = participants / capacity
            
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            
            if utilization_rate > 1.0:
                return RuleCheckResult(
                    status=RuleStatus.FAIL,
                    score_impact=-25,
                    message=f"参与人数({participants})超过教室容量({capacity})",
                    details={
                        "participants": participants,
                        "capacity": capacity,
                        "utilization_rate": utilization_rate
                    },
                    execution_time_ms=execution_time
                )
            elif utilization_rate > 0.9:
                return RuleCheckResult(
                    status=RuleStatus.WARNING,
                    score_impact=5,
                    message=f"教室利用率较高({utilization_rate:.1%})",
                    details={
                        "participants": participants,
                        "capacity": capacity,
                        "utilization_rate": utilization_rate
                    },
                    execution_time_ms=execution_time
                )
            elif utilization_rate < 0.1:
                return RuleCheckResult(
                    status=RuleStatus.WARNING,
                    score_impact=0,
                    message=f"教室利用率较低({utilization_rate:.1%})，建议选择小教室",
                    details={
                        "participants": participants,
                        "capacity": capacity,
                        "utilization_rate": utilization_rate
                    },
                    execution_time_ms=execution_time
                )
            else:
                score = 10 if 0.5 <= utilization_rate <= 0.8 else 5
                return RuleCheckResult(
                    status=RuleStatus.PASS,
                    score_impact=score,
                    message=f"人数安排合理({utilization_rate:.1%}利用率)",
                    details={
                        "participants": participants,
                        "capacity": capacity,
                        "utilization_rate": utilization_rate
                    },
                    execution_time_ms=execution_time
                )
                
        except Exception as e:
            execution_time = (datetime.now() - start_time).total_seconds() * 1000
            logger.error(f"人数验证失败: {str(e)}")
            return RuleCheckResult(
                status=RuleStatus.WARNING,
                score_impact=-3,
                message=f"人数验证异常: {str(e)}",
                execution_time_ms=execution_time
            )


class RuleEngine:
    """规则引擎主类."""
    
    def __init__(self):
        self.rules: List[BaseRule] = []
        self._initialize_default_rules()
        
    def _initialize_default_rules(self):
        """初始化默认规则集."""
        self.rules = [
            TimeConflictChecker(),
            ApplicantQualificationChecker(),
            PurposeValidator(),
            EquipmentMatcher(),
            TimeRangeValidator(),
            ParticipantCountValidator()
        ]
        
        logger.info(f"已初始化 {len(self.rules)} 个默认审核规则")
    
    def add_rule(self, rule: BaseRule):
        """添加规则."""
        self.rules.append(rule)
        logger.info(f"添加规则: {rule.name}")
    
    def remove_rule(self, rule_id: str):
        """移除规则."""
        self.rules = [r for r in self.rules if r.rule_id != rule_id]
        logger.info(f"移除规则: {rule_id}")
    
    def get_rule(self, rule_id: str) -> Optional[BaseRule]:
        """获取指定规则."""
        return next((r for r in self.rules if r.rule_id == rule_id), None)
    
    def execute_rules(self, request: AuditRequest) -> List[RuleResult]:
        """执行所有规则检查."""
        results = []
        
        # 按优先级排序执行
        sorted_rules = sorted(
            [r for r in self.rules if r.is_active],
            key=lambda x: (x.priority.value, x.name)
        )
        
        for rule in sorted_rules:
            try:
                logger.debug(f"执行规则: {rule.name}")
                check_result = rule.check(request)
                
                rule_result = RuleResult(
                    rule_id=rule.rule_id,
                    rule_name=rule.name,
                    matched=(check_result.status == RuleStatus.PASS),
                    score_impact=check_result.score_impact,
                    message=check_result.message,
                    execution_time_ms=check_result.execution_time_ms
                )
                
                results.append(rule_result)
                
                # 如果是阻塞规则且失败，停止后续规则执行
                if rule.is_blocking and check_result.status == RuleStatus.FAIL:
                    logger.warning(f"阻塞规则失败，停止执行: {rule.name}")
                    break
                    
            except Exception as e:
                logger.error(f"规则执行异常 {rule.name}: {str(e)}")
                error_result = RuleResult(
                    rule_id=rule.rule_id,
                    rule_name=rule.name,
                    matched=False,
                    score_impact=-10,
                    message=f"规则执行异常: {str(e)}",
                    execution_time_ms=0
                )
                results.append(error_result)
        
        logger.info(f"规则执行完成，共执行 {len(results)} 个规则")
        return results
    
    def calculate_total_score(self, rule_results: List[RuleResult]) -> float:
        """计算总分数."""
        total_score = sum(result.score_impact for result in rule_results)
        
        # 归一化到0-100分
        normalized_score = max(0, min(100, 50 + total_score))
        
        logger.debug(f"总分计算: 原始分 {total_score}, 归一化分 {normalized_score}")
        return normalized_score
    
    def get_active_rules_info(self) -> List[Dict[str, Any]]:
        """获取激活规则信息."""
        return [
            {
                "rule_id": rule.rule_id,
                "name": rule.name,
                "priority": rule.priority.value,
                "weight": rule.weight,
                "is_blocking": rule.is_blocking,
                "is_active": rule.is_active
            }
            for rule in self.rules
        ]